/*
 * Copyright (c) 2010-2011, Yawning <yawninglol at gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the Mew Developers nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package yawning.mew.pivot.feraldruid;

import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;

import org.apache.pivot.collections.HashMap;
import org.apache.pivot.collections.LinkedList;
import org.apache.pivot.collections.List;
import org.apache.pivot.collections.Map;
import org.apache.pivot.json.JSON;
import org.apache.pivot.json.JSONSerializer;
import org.apache.pivot.serialization.SerializationException;

import yawning.mew.ProcEffect;
import yawning.mew.Procs;
import yawning.mew.ProcEffect.Effect;
import yawning.mew.character.Profession;

public class Loadables {
	protected Loadables() {
		mLevelLoadableMap = new HashMap<Integer,LevelLoadables>();
		doLoad();
	}

	private static Loadables mInstance = null;
	private static final String mProcFileNameLevel85 = "yawning/mew/pivot/feraldruid/Procs85.json";
	private static final String mConsumableFileNameLevel85 = "yawning/mew/pivot/feraldruid/Consumables85.json";

	protected class ProcProfProp {
		public ProcProfProp(Profession p, int s, boolean perk) {
			mProf = p;
			mMinSkill = s;
			mIsPerk = perk;
		}

		Profession mProf;
		int mMinSkill;
		boolean mIsPerk;
	}

	public class LevelLoadables {
		public LevelLoadables() {
			mProcList = new LinkedList<Map<String,Object>>();
			mEnchantMap = new HashMap<Integer,String>();
			mProfMap = new HashMap<String,ProcProfProp>();
			mFoodMap = new HashMap<String,Object>();
			mFlaskMap = new HashMap<String,Object>();
			mPotionMap = new HashMap<String,Object>();
			mFoodList = new LinkedList<String>();
			mFlaskList = new LinkedList<String>();
			mPotionList = new LinkedList<String>();
		}

		protected LinkedList<Map<String,Object>> mProcList;
		protected HashMap<Integer,String> mEnchantMap;
		protected HashMap<String,ProcProfProp> mProfMap;

		protected LinkedList<String> mFoodList;
		protected LinkedList<String> mFlaskList;
		protected LinkedList<String> mPotionList;
		protected HashMap<String,Object> mFoodMap;
		protected HashMap<String,Object> mFlaskMap;
		protected HashMap<String,Object> mPotionMap;
	}

	private HashMap<Integer,LevelLoadables> mLevelLoadableMap;

	public static synchronized Loadables getInstance() {
        if (mInstance == null) {
            mInstance = new Loadables();
        }

        return mInstance;
    }

	public LevelLoadables get(int level) {
		return mLevelLoadableMap.get(level);
	}

	private void doLoad() {
		LevelLoadables tmp;

		tmp = new LevelLoadables();
		mLevelLoadableMap.put(85, tmp);
		loadConsumables(mConsumableFileNameLevel85, 85);
		loadProcs(mProcFileNameLevel85, 85);
	}

	@SuppressWarnings("unchecked")
	private void loadProcs(String fileName, int level) {
		List<Map<String,Object>> backingStore;
		JSONSerializer jsonSerializer = new JSONSerializer();
		LevelLoadables rval = mLevelLoadableMap.get(level);

		try {
			InputStream istream = this.getClass().getClassLoader().getResourceAsStream(fileName);

			backingStore = (List<Map<String,Object>>) jsonSerializer.readObject(istream);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (SerializationException e) {
			throw new RuntimeException(e);
		}

		Procs procs = Procs.getInstance();
		ProcEffect p;

		for (Map<String,Object> row: (List<Map<String,Object>>) backingStore) {
			String strId = JSON.get(row, "prop");

			// Do some parsing here.
			boolean isSpell = (strId.charAt(0) == 's');
			int spellId = Integer.parseInt(strId.substring(1));
			String triggers[] = ((String) JSON.get(row, "triggers")).split(",");

			p = new ProcEffect();
			p.name = JSON.get(row, "name");
			p.triggers = EnumSet.noneOf(ProcEffect.Trigger.class);
			for (int i = 0; i < triggers.length; i++) {
				if (triggers[i].equals("USER")) { p.triggers.add(ProcEffect.Trigger.USER); }
				if (triggers[i].equals("WHITE")) { p.triggers.add(ProcEffect.Trigger.WHITE); }
				if (triggers[i].equals("WHITECRIT")) { p.triggers.add(ProcEffect.Trigger.WHITECRIT); }
				if (triggers[i].equals("YELLOW")) { p.triggers.add(ProcEffect.Trigger.YELLOW); }
				if (triggers[i].equals("YELLOWCRIT")) { p.triggers.add(ProcEffect.Trigger.YELLOWCRIT); }
				if (triggers[i].equals("DOT")) { p.triggers.add(ProcEffect.Trigger.DOT); }
				if (triggers[i].equals("DOTCRIT")) { p.triggers.add(ProcEffect.Trigger.DOTCRIT); }
			}

			if (row.containsKey("enchantId")) rval.mEnchantMap.put(JSON.getInt(row, "enchantId"), strId);

			if (row.containsKey("requiresCode")) {
				boolean requiresCode = (Boolean) JSON.get(row, "requiresCode");
				if (requiresCode) {
					p.set(Effect.SPELL_ID, spellId);
				}
			}

			if (row.containsKey("rate")) { p.rate = JSON.getDouble(row, "rate"); }
			if (row.containsKey("ppm")) { p.ppm = JSON.getDouble(row, "ppm"); }

			if (row.containsKey("buffCooldown")) { p.buffCooldown = JSON.getInt(row, "buffCooldown"); }
			if (row.containsKey("buffLinkedCooldown")) { p.buffLinkedCooldown = JSON.getInt(row, "buffLinkedCooldown"); }
			if (row.containsKey("buffDuration")) { p.buffDuration = JSON.getInt(row, "buffDuration"); }
			if (row.containsKey("buffStackMax")) { p.buffStackMax = JSON.getInt(row, "buffStackMax"); }

			if (row.containsKey("buffStrength")) { p.set(Effect.STRENGTH, JSON.getInt(row, "buffStrength")); }
			if (row.containsKey("buffAgility")) { p.set(Effect.AGILITY, JSON.getInt(row, "buffAgility")); }
			if (row.containsKey("buffAttackPower")) { p.set(Effect.ATTACK_POWER, JSON.getInt(row, "buffAttackPower")); }
			if (row.containsKey("buffHasteRating")) { p.set(Effect.HASTE_RATING, JSON.getInt(row, "buffHasteRating")); }
			if (row.containsKey("buffCritRating")) { p.set(Effect.CRIT_RATING, JSON.getInt(row, "buffCritRating")); }
			if (row.containsKey("buffHitRating")) { p.set(Effect.HIT_RATING, JSON.getInt(row, "buffHitRating")); }
			if (row.containsKey("buffExpertiseRating")) { p.set(Effect.EXPERTISE_RATING, JSON.getInt(row, "buffExpertiseRating")); }
			if (row.containsKey("buffMasteryRating")) { p.set(Effect.MASTERY_RATING, JSON.getInt(row, "buffMasteryRating")); }
			if (row.containsKey("buffDodgeRating")) { p.set(Effect.DODGE_RATING, JSON.getInt(row, "buffDodgeRating")); }
			if (row.containsKey("buffWeaponDamage")) { p.set(Effect.WEAPON_DAMAGE, JSON.getInt(row, "buffWeaponDamage")); }
			if (row.containsKey("damagePhys")) { p.set(Effect.PHYSICAL_DAMAGE, JSON.getInt(row, "damagePhys")); }
			if (row.containsKey("damageSpell")) { p.set(Effect.SPELL_DAMAGE, JSON.getInt(row, "damageSpell")); }

		    // Register it.
		    if (isSpell) {
		    	procs.registerProcBySpellID(spellId, p);
		    } else {
		    	procs.registerProcByItemID(spellId, p);
		    }

		    // Handle stuff that's tied to professions
		    if (row.containsKey("profId")) {
		    	Profession prof = Profession.get(JSON.getInt(row, "profId"));
		    	int minSkill = row.containsKey("profSkill") ? JSON.getInt(row,"profSkill") : 0;
		    	boolean isPerk = row.containsKey("profPerk") ? (Boolean) JSON.get(row, "profPerk") : false;

		    	rval.mProfMap.put(strId, new ProcProfProp(prof, minSkill, isPerk));
		    }

		    // Add the entry to the backing store.
		    HashMap<String,Object> tmp = new HashMap<String,Object>(row);
		    tmp.put("enabled", false);
		    rval.mProcList.add(tmp);
		}
	}

	@SuppressWarnings("unchecked")
	private void loadConsumables(String fileName, int level) {
		List<Map<String,Object>> backingStore;
		JSONSerializer jsonSerializer = new JSONSerializer();
		LevelLoadables rval = mLevelLoadableMap.get(level);

		try {
			InputStream istream = this.getClass().getClassLoader().getResourceAsStream(fileName);

			backingStore = (List<Map<String,Object>>) jsonSerializer.readObject(istream);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (SerializationException e) {
			throw new RuntimeException(e);
		}

		for (Map<String,Object> row: (List<Map<String,Object>>) backingStore) {
			String name = JSON.get(row, "name");
			String strId = JSON.get(row, "prop");

			// Do some parsing here.
			boolean isFood = (strId.charAt(0) == 'f');
			boolean isElixir = (strId.charAt(0) == 'e');
			boolean isPotion = (strId.charAt(0) == 'p');

			if (isFood)	{
				rval.mFoodList.add(name);
				rval.mFoodMap.put(name, row);
			}
			if (isElixir) {
				rval.mFlaskList.add(name);
				rval.mFlaskMap.put(name, row);
			}
			if (isPotion) {
				rval.mPotionList.add(name);
				rval.mPotionMap.put(name, row);

				// Load potions into the Mew core proc datastore as well.
				int spellId = Integer.parseInt(strId.substring(1));

				ProcEffect p = new ProcEffect();
				p.name = name;
				p.triggers = EnumSet.of(ProcEffect.Trigger.POTION);

				if (row.containsKey("buffDuration")) { p.buffDuration = JSON.getInt(row, "buffDuration"); }

				if (row.containsKey("buffStrength")) { p.set(Effect.STRENGTH, JSON.getInt(row, "buffStrength")); }
				if (row.containsKey("buffAgility")) { p.set(Effect.AGILITY, JSON.getInt(row, "buffAgility")); }
				if (row.containsKey("buffAttackPower")) { p.set(Effect.ATTACK_POWER, JSON.getInt(row, "buffAttackPower")); }
				if (row.containsKey("buffHasteRating")) { p.set(Effect.HASTE_RATING, JSON.getInt(row, "buffHasteRating")); }
				if (row.containsKey("buffCritRating")) { p.set(Effect.CRIT_RATING, JSON.getInt(row, "buffCritRating")); }
				if (row.containsKey("buffHitRating")) { p.set(Effect.HIT_RATING, JSON.getInt(row, "buffHitRating")); }
				if (row.containsKey("buffExpertiseRating")) { p.set(Effect.EXPERTISE_RATING, JSON.getInt(row, "buffExpertiseRating")); }
				if (row.containsKey("buffMasteryRating")) { p.set(Effect.MASTERY_RATING, JSON.getInt(row, "buffMasteryRating")); }

				Procs.getInstance().registerProcByPotionID(spellId, p);
			}
		}
	}
}
